#include "opcuanodeselect.h"
#include "ui_opcuanodeselect.h"

#include <QMetaEnum>
// #define ROOT_NODEID "ns=0;i=84" // ROOT
#define ROOT_NODEID "ns=0;i=85" // OBJECT

OpcUaNodeSelect::OpcUaNodeSelect(QOpcUaClient *client, QWidget *parent) : QDialog(parent)
    , ui(new Ui::OpcUaNodeSelect)
    , m_client(client) {
    ui->setupUi(this);

    this->setWindowTitle("选择OPC UA节点");

    model = new QStandardItemModel(ui->treeView);
    model->setHorizontalHeaderLabels(QStringList() << "节点" << "值" << "数据类型" << "说明" << "节点类型" << "节点ID" );
    ui->treeView->setModel(model);
    // ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);// 界面刷新速度奇慢，不能用
    ui->treeView->header()->setSectionResizeMode(QHeaderView::Interactive);
    ui->treeView->setColumnWidth(0, 220);
    ui->treeView->setColumnWidth(1, 100);
    ui->treeView->setColumnWidth(2, 60);
    ui->treeView->setColumnWidth(3, 220);
    ui->treeView->setColumnWidth(4, 60);
    ui->treeView->setColumnWidth(5, 220);
    // ui->treeView->header()->setSectionResizeMode(3, QHeaderView::Fixed);
    // 排序设置
    // ui->treeView->setSortingEnabled(true);
    // ui->treeView->header()->setSortIndicatorShown(true);
    // ui->treeView->header()->setSortIndicator(0, Qt::AscendingOrder);

    ui->treeView->setColumnWidth(3, 200);

    pBar = new QProgressBar(ui->treeView);
    pBar->hide();

    addRootToTree();
}

OpcUaNodeSelect::~OpcUaNodeSelect() {
    delete pBar;
    delete model;
    delete ui;
}

void OpcUaNodeSelect::on_treeView_clicked(const QModelIndex &index) {
    // 友邻第0列的QModelIndex
    QModelIndex columnX = index.siblingAtColumn(0);
    browseName = model->data(columnX, Qt::DisplayRole).toString();
    columnX = index.siblingAtColumn(1);
    value      = model->data(columnX, Qt::DisplayRole).toString();
    columnX = index.siblingAtColumn(4);
    nodeClass  = model->data(columnX, Qt::DisplayRole).toString();
    columnX = index.siblingAtColumn(2);
    dataType   = model->data(columnX, Qt::DisplayRole).toString();
    columnX = index.siblingAtColumn(5);
    nodeId     = model->data(columnX, Qt::DisplayRole).toString();
    columnX = index.siblingAtColumn(3);
    description = model->data(columnX, Qt::DisplayRole).toString();

    ui->infoText->setText(QString("当前节点：%1, %2, %3, %4")
                          .arg(description.isEmpty() ? browseName : description, value.left(30), dataType, nodeId));
    // 只能添加dataType为variable类型的变量
    ui->btnSelected->setEnabled(nodeClass.left(8) == "Variable");
}

QString OpcUaNodeSelect::getValue(int role) {
    switch (role) {
    case Qt::DisplayRole:
        return browseName;
    case Qt::UserRole + 1:
        return value;
    case Qt::UserRole + 2:
        return nodeClass;
    case Qt::UserRole + 3:
        return dataType;
    case Qt::UserRole + 4:
        return nodeId;
    case Qt::UserRole + 6:
        return description;
    default:
        return NULL;
    }
}

void OpcUaNodeSelect::on_btnSelected_clicked() {
    done(Accepted);
}


void OpcUaNodeSelect::on_btnCancel_clicked() {
    done(Rejected);
}

// 首次运行时，读取OPCUA根节 browseName，并将browseName加入 treeview 根节点
// 再调用loadChildrenNode(rootItem)读取下一级子节点，加到 treeview 根节点下面作为二级节点
void OpcUaNodeSelect::addRootToTree() {
    if (m_client->state() == QOpcUaClient::ClientState::Connected) {
        root_node = m_client->node(ROOT_NODEID);// 根节点
        if (root_node) {
            qDebug() << "('" << ROOT_NODEID << "')根节点被创建。";

            connect(root_node, &QOpcUaNode::attributeRead, this, [ = ](QOpcUa::NodeAttributes attr) {
                Q_UNUSED(attr);
                QString root_browseName = root_node->attribute(QOpcUa::NodeAttribute::BrowseName).value<QOpcUaQualifiedName>().name();

                // 创建 treeview 的根节点，并将OPCUA根节点的NODE指针存入ITEM用户存储区，再读取２级节点
                QStandardItem *rootItem = new QStandardItem(root_browseName);
                QVariant root_node_pointor =  QVariant::fromValue((void *) root_node);
                rootItem->setData(root_node_pointor, Qt::UserRole + 100);
                // 加入根节点->treeview
                model->appendRow(rootItem);
                // 读取下一级子节点
                loadChildrenNode(rootItem);
            });

            // 读取 root 节点下子节点的 browname 属性值
            root_node->readAttributes(QOpcUa::NodeAttribute::BrowseName);
        }
    }
}

// 读取parentItem的下一级OPCUA子节点（）
void OpcUaNodeSelect::loadChildrenNode(QStandardItem *parentItem) {
    if(parentItem->hasChildren())
        return;

    /* 将对象存储在用户数据区中的方法：
     *      QStandardItem *rootItem = new QStandardItem(browseName);
     *      QVariant root_node_pointor =  QVariant::fromValue((void *) root_node);
     *      rootItem->setData(root_node_pointor, Qt::UserRole + 100);
     */
    // 首先提取 parentItem 中保存的 parentNode 对象
    QVariant t_NodePointor    = parentItem->data(Qt::UserRole + 100);
    QOpcUaNode *parentNode = (QOpcUaNode*)t_NodePointor.value<void*>();

    // browseChildren()槽函数。获得 parentNode 对象的子节点后，获取每个子节点的属性，并加入到model中
    connect(parentNode, &QOpcUaNode::browseFinished, this, [ = ](const QList<QOpcUaReferenceDescription> &childrens, QOpcUa::UaStatusCode statusCode) {
        if (statusCode != QOpcUa::Good) {
            qWarning() << "Browsing node" << root_node->nodeId() << "finally failed:" << statusCode;
            return;
        }

        for (const auto &item : childrens) {
            // 依次获取子节点的 nodeId
            QString children_nodeid = item.targetNodeId().nodeId();
            // 创建到子节点的连接
            QOpcUaNode *children_node = m_client->node(children_nodeid);

            // readAttributes()槽函数。
            connect(children_node, &QOpcUaNode::attributeRead, this, [ = ](QOpcUa::NodeAttributes attr) {

                QString children_browsename, children_nodeclass, children_value, children_datatype, children_description;

                if(attr & QOpcUa::NodeAttribute::BrowseName) {
                    children_browsename = children_node->attribute(QOpcUa::NodeAttribute::BrowseName).value<QOpcUaQualifiedName>().name();
                }
                if(attr & QOpcUa::NodeAttribute::NodeClass) {
                    QMetaEnum metaEnum = QMetaEnum::fromType<QOpcUa::NodeClass>();
                    children_nodeclass = metaEnum.valueToKey(int(children_node->attribute(QOpcUa::NodeAttribute::NodeClass).value<QOpcUa::NodeClass>()));
                }
                if(attr & QOpcUa::NodeAttribute::Value) {
                    children_value = children_node->attribute(QOpcUa::NodeAttribute::Value).value<QString>();
                    // 如果browsename == "_Description"，就更新其父节点的Description列（第3列）内容
                    if((children_browsename == "_Description") && (!children_value.isEmpty())) {
                        model->setData(parentItem->index().siblingAtColumn(3), children_value);
                        // ui->textEdit->append(children_value);
                    }
                }
                if(attr & QOpcUa::NodeAttribute::DataType) {
                    const QString typeId = children_node->attribute(QOpcUa::NodeAttribute::DataType).toString();
                    auto enumEntry = QOpcUa::namespace0IdFromNodeId(typeId);  // 将 "ns=0;i=10" 转换成 QOpcUa::NodeIds::Namespace0::Float
                    children_datatype = QOpcUa::namespace0IdName(enumEntry);  // 再将 QOpcUa::NodeIds::Namespace0::Float 转换成 "Float"
                }
                if(attr & QOpcUa::NodeAttribute::Description) {
                    children_description = children_node->attribute(QOpcUa::NodeAttribute::Description).value<QOpcUaLocalizedText>().text();
                }

                QList<QStandardItem*> itemList; /* "项目名" << "值" << "数据类型" << "说明" << "节点类型" << "节点ID"   */
                QStandardItem *children_browsename_item = new QStandardItem(children_browsename);
                QStandardItem *children_nodeclass_item = new QStandardItem(children_nodeclass);
                QStandardItem *children_value_item = new QStandardItem(children_value);
                QStandardItem *children_datatype_item = new QStandardItem(children_datatype);
                QStandardItem *children_nodeid_item = new QStandardItem(children_node->nodeId());
                QStandardItem *children_description_item = new QStandardItem(children_description);
                itemList << children_browsename_item
                         << children_value_item
                         << children_datatype_item
                         << children_description_item
                         << children_nodeclass_item
                         << children_nodeid_item;

                // 将 children_node 对象指针存储到 children_browsename_item 的用户数据中
                QVariant children_node_pointor =  QVariant::fromValue((void *) children_node);
                children_browsename_item->setData(children_node_pointor, Qt::UserRole + 100);

                //
                parentItem->appendRow(itemList);
            });

            // 依次读取子节点children_node下的子节点的5种属性值，加入到model中
            children_node->readAttributes(QOpcUa::NodeAttribute::BrowseName
                                          | QOpcUa::NodeAttribute::NodeClass
                                          | QOpcUa::NodeAttribute::Value
                                          | QOpcUa::NodeAttribute::Description
                                          | QOpcUa::NodeAttribute::DataType);
        }
    });
    // 获取 parentNode 对象的子节点
    parentNode->browseChildren();
}

// 当treeview的某节点展开时，读取该节点的所有子节点
void OpcUaNodeSelect::on_treeView_expanded(const QModelIndex &index) {
    QStandardItem *item = model->itemFromIndex(index);
    if(item->hasChildren()) {
        int rCount = item->rowCount();

        // 读取过程进度条
        pBar->setMinimum(0);
        pBar->setMaximum(rCount - 1);
        pBar->setGeometry((ui->treeView->height() - 50) / 2, (ui->treeView->width() - 260) / 2, 260, 23);
        pBar->show();


        ui->infoText->setText(QString("发现 %1 个子节点，正在读取，请稍候...").arg(item->rowCount()));
        QDateTime beginTime = QDateTime::currentDateTime(); //获取系统当前的时间

        // 构造model数据时暂停刷新界面
        ui->treeView->setUpdatesEnabled(false);
        // 依次读取每个子节点
        for(int i = 0; i < rCount; i++) {
            QStandardItem *child_item = item->child(i);
            loadChildrenNode(child_item);
            pBar->setValue(i);
            QCoreApplication::processEvents();
        }
        ui->treeView->setUpdatesEnabled(true);

        pBar->hide();
        QDateTime endTime = QDateTime::currentDateTime();
        qint64 t = beginTime.msecsTo(endTime);
        QString stime = t > 1000 ? QString("%1s").arg(t / 1000.0) : QString("%1ms").arg(t);
        ui->infoText->setText(QString("成功读取 %1 个子节点，接收耗时：%2").arg(rCount).arg(stime));
        qDebug()  << "子节点数量：" << rCount << "，接收共耗时：" << stime;
    }
}

